<?php

namespace zhuzixian520\meilisearch;

use yii\base\Component;
use yii\base\InvalidArgumentException;

/**
 * The Command class implements the API for accessing the Meilisearch REST API.
 *
 * @property-read bool|string|mixed $health health of Meilisearch server
 * @property-read bool|string|mixed $tasks All tasks globally, regardless of index
 * @property-read bool|string|mixed $version Version of Meilisearch
 * @property-read bool|string|mixed $keys All existing API keys
 * @property-read bool|string|mixed $stats Stats of all indexes
 * @property-read bool|string|mixed $allIndexes All indexes.
 * @author Trevor <zhuzixian520@126.com>
 */
class Command extends Component
{
    const ROUTE_INDEXES = 'indexes';
    const ROUTE_TASKS = 'tasks';
    const ROUTE_KEYS = 'keys';
    const ROUTE_STATS = 'stats';
    const ROUTE_HEALTH = 'health';
    const ROUTE_VERSION = 'version';
    const ROUTE_DUMPS = 'dumps';

    /**
     * @var Connection
     */
    public $db;

    /**
     * Get an index instance
     *
     * @param string $index_uid The index unique identifier (mandatory)
     * @return IndexesCommand
     */
    public function index($index_uid)
    {
        $config = [
            'db' => $this->db,
            'index_uid' => $index_uid,
        ];
        return new IndexesCommand($config);
    }

    /**
     * List all indexes.
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/indexes.html#list-all-indexes
     */
    public function getAllIndexes()
    {
        return $this->db->get(self::ROUTE_INDEXES);
    }

    /**
     * Create an index
     *
     * @param string $index_uid The index unique identifier (mandatory)
     * @param string|null $primaryKey The primary key of the documents
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/indexes.html#create-an-index
     */
    public function createIndex($index_uid, $primaryKey=null)
    {
        $body['uid'] = $index_uid;

        if ($primaryKey) $body['primaryKey'] = $primaryKey;

        return $this->db->post(self::ROUTE_INDEXES, [], json_encode($body));
    }

    /**
     * Update an index's primary key
     *
     * @param string $index_uid The index unique identifier (mandatory)
     * @param string|null $primaryKey The primary key of the documents
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/indexes.html#update-an-index
     */
    public function updateIndex($index_uid, $primaryKey=null)
    {
        return $this->index($index_uid)->update($primaryKey);
    }

    /**
     * Delete an index
     *
     * @param string $index_uid The index unique identifier (mandatory)
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/indexes.html#delete-an-index
     */
    public function deleteIndex($index_uid)
    {
        return $this->index($index_uid)->delete();
    }

    /**
     * List all tasks globally, regardless of index
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/tasks.html#get-all-tasks
     */
    public function getTasks()
    {
        return $this->db->get(self::ROUTE_TASKS);
    }

    /**
     * Get a single task
     *
     * @param int $task_uid The task identifier
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/tasks.html#get-task
     */
    public function getTask($task_uid)
    {
        if (empty($task_uid)) throw new InvalidArgumentException('Argument $task_uid is empty');

        return $this->db->get([self::ROUTE_TASKS, $task_uid]);
    }

    /**
     * List all existing API keys
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/keys.html#get-all-keys
     */
    public function getKeys()
    {
        return $this->db->get(self::ROUTE_KEYS);
    }

    /**
     * Get information on the specified key
     *
     * @param string $key Alphanumeric value generated by Meilisearch
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/keys.html#get-one-key
     */
    public function getKey($key)
    {
        if (empty($key)) throw new InvalidArgumentException('Argument $key is empty');

        return $this->db->get([self::ROUTE_KEYS, $key]);
    }

    /**
     * Create an API key with the provided description, permissions, and expiration date
     *
     * @param array $body
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/keys.html#create-a-key
     */
    public function createKey($body)
    {
        if (empty($body)) throw new InvalidArgumentException('Argument $body is empty');

        return $this->db->post(self::ROUTE_KEYS, [], json_encode($body));
    }

    /**
     * Update the description, permissions, or expiration date of an API key
     *
     * @param string $key Alphanumeric value generated by Meilisearch
     * @param array $body
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/keys.html#update-a-key
     */
    public function updateKey($key, $body)
    {
        if (empty($key)) throw new InvalidArgumentException('Argument $key is empty');
        if (empty($body)) throw new InvalidArgumentException('Argument $body is empty');

        return $this->db->patch([self::ROUTE_KEYS, $key], [], json_encode($body));
    }

    /**
     * Delete the specified API key
     *
     * @param string $key Alphanumeric value generated by Meilisearch
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/keys.html#delete-a-key
     */
    public function deleteKey($key)
    {
        if (empty($key)) throw new InvalidArgumentException('Argument $key is empty');

        return $this->db->delete([self::ROUTE_KEYS, $key]);
    }

    /**
     * Get stats of all indexes
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/stats.html#get-stats-of-all-indexes
     */
    public function getStats()
    {
        return $this->db->get(self::ROUTE_STATS);
    }

    /**
     * Get health of Meilisearch server.
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/health.html#get-health
     */
    public function getHealth()
    {
        return $this->db->get(self::ROUTE_HEALTH);
    }

    /**
     * Get version of Meilisearch
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/version.html#get-version-of-meilisearch
     */
    public function getVersion()
    {
        return $this->db->get(self::ROUTE_VERSION);
    }

    /**
     * Triggers a dump creation process
     *
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/dump.html#create-a-dump
     */
    public function createDump()
    {
        return $this->db->post(self::ROUTE_DUMPS);
    }

    /**
     * Get the status of a dump creation process using the uid returned after calling the dump creation route
     *
     * @param string $dump_uid Dump unique id generated by Meilisearch
     * @return bool|mixed|string
     * @throws Exception
     * @see https://docs.meilisearch.com/reference/api/dump.html#get-dump-status
     */
    public function getDumpStatus($dump_uid)
    {
        if (empty($dump_uid)) throw new InvalidArgumentException('Argument $dump_uid is empty');

        return $this->db->get([self::ROUTE_DUMPS, $dump_uid, 'status']);
    }
}